package Serializadores;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;


import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;


import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import tp3.*;

public class SerializarTablero {
	
	public Element serializar(Tablero tablero, String nombre) throws ParserConfigurationException{
		
		//Nivel 1 
		
		int ancho = tablero.getAncho();
		int alto = tablero.getAlto();
		int cantMaxMovimientos = tablero.getCantMaxMovimientos();
		int factorPuntos =tablero.getFactorPuntos();
		
		List<Esquina> esquinas = tablero.getEsquinas();
	
		int posicionEsquinaInicial = esquinas.indexOf(tablero.getEsquinaInicial()); 
		int posicionEsquinaFinal = esquinas.indexOf(tablero.getEsquinaFinal());
		
		
		DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder docBuilder = docFactory.newDocumentBuilder();

		// Parse de int a String de dimensionws
		String cadenaAncho = "";
		cadenaAncho = String.valueOf(ancho);
		String cadenaAlto = "";
		cadenaAlto = String.valueOf(alto);
		
		String esquinaIni = "";
		esquinaIni = String.valueOf(posicionEsquinaInicial);
		String esquinaFin = "";
		esquinaFin = String.valueOf(posicionEsquinaFinal);
		
		String puntos = "";
		puntos = String.valueOf(factorPuntos);
		String movimientosMaximos = "";
		movimientosMaximos = String.valueOf(cantMaxMovimientos);
		//-----------------------------------------------
	
		// Tag principal
		Document doc = docBuilder.newDocument();
		Element raiz = doc.createElement("Tablero");
		
		doc.appendChild(raiz);

		// Tag Dimensiones
		Element dimensiones = doc.createElement("Dimensiones");
		raiz.appendChild(dimensiones);
	
		// Valores del Tag "dimensiones"
		dimensiones.setAttribute("ancho", cadenaAncho);
		dimensiones.setAttribute("alto", cadenaAlto);
		
		
		//EsquinaInicialYFinal
		System.out.println(esquinaIni+ " "+ esquinaFin);
		Element salida = doc.createElement("Partida");
		salida.setAttribute("salida", esquinaIni);
 	 	Element llegada = doc.createElement("Meta");
 	 	llegada.setAttribute("llegada", esquinaFin);
 	 	raiz.appendChild(salida);
 	 	raiz.appendChild(llegada);
		
		// factor puntaje y cantidad de movimientos maxima.-
 	 	Element cantidadMovimientos = doc.createElement("CantidadDeMovimientosMaximo");
 	 	cantidadMovimientos.setAttribute("maximo", movimientosMaximos);
 	 	
 	 	Element factorPuntaje = doc.createElement("FactorPuntaje");
 	 	factorPuntaje.setAttribute("factor", puntos);
	
 	 	raiz.appendChild(cantidadMovimientos);
 	 	raiz.appendChild(factorPuntaje);
		
		/*Acá se van a agregar los tag correspondientes a los Obstaculos/sopresas*/
		/*Obteniendo primero una lista de cuadras que tengan obstaculos/sorpesas*/
		
		List<Cuadra> cuadras = getCuadras(tablero);//metodo privado que devuelve las cuadras del tablero
		limpiarCuadras(cuadras);// deja en la lista de cuadras solo las cuadras que tengan obstaculos/sorpresas
	
		
		
		
		Iterator<Cuadra> it= cuadras.iterator();
		
		while( it.hasNext()){
			
			Cuadra unaCuadra= it.next();
			//por cada cuadra obtengo sus referencias a los obstaculos/sorpresas
			Obstaculo unObstaculo=unaCuadra.getObstaculo();
			Sorpresa unaSorpresa= unaCuadra.getSorpresa();
			
			
			//por cada cuadra guardo sus Esquinas ( en forma de String )
			 posicionEsquinaInicial = esquinas.indexOf(unaCuadra.getEsquinaInicial()); // cargo la posicion de la esquina para luego guardarla en el tag correspondiente
			 posicionEsquinaFinal = esquinas.indexOf(unaCuadra.esquinaOpuesta(unaCuadra.getEsquinaInicial()));
			String EsquinaInicial = "";
			EsquinaInicial = String.valueOf(posicionEsquinaInicial);
			String EsquinaFinal = "";
			EsquinaFinal = String.valueOf(posicionEsquinaFinal);
			
			
			//siempre y cuando haya un obstaculo/sorpresa, llamo a serializar de las respectivas clases
			if(unObstaculo != null){
				
				Element obstaculos = doc.createElement("Obstaculos");
				raiz.appendChild(obstaculos);
				obstaculos.setAttribute("PosicionA", EsquinaInicial);
				obstaculos.setAttribute("PosicionB", EsquinaFinal);
				Element estadoDeObstaculo = doc.createElement(unObstaculo.getClass().getName());
				obstaculos.appendChild(estadoDeObstaculo);
			}
			
			if(unaSorpresa != null){
				
				Element sorpresas = doc.createElement("Sorpresa");
				raiz.appendChild(sorpresas);
				sorpresas.setAttribute("PosicionA", EsquinaInicial);
				sorpresas.setAttribute("PosicionB", EsquinaFinal);
				Element estadoDeSorpresa = doc.createElement(unaSorpresa.getClass().getName());
				sorpresas.appendChild(estadoDeSorpresa);
			}
		}
		
		
	
		try{
		String nombreDelArchivo = nombre + ".xml";	
		// creo el archivo xml
		TransformerFactory transformerFactory = TransformerFactory.newInstance();
		Transformer transformer = transformerFactory.newTransformer();
		DOMSource source = new DOMSource(doc);
		StreamResult result = new StreamResult(new File(nombreDelArchivo));

		transformer.transform(source, result);
		}
		catch (TransformerException tfe) {
		tfe.printStackTrace();
	  }
		

	 
	return raiz;
	
	
	}

	

	
	public Tablero deserializar(String nombreDelArchivo) throws ParserConfigurationException, SAXException, IOException{
		
		String archivo = nombreDelArchivo+".xml";
		
		
		
			//inicializo 
			File stocks = new File(archivo);
			DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
			Document doc = dBuilder.parse(stocks);
			doc.getDocumentElement().normalize();
			 // cargar dimensiones y crear tablero
			NodeList nodes = doc.getElementsByTagName("Dimensiones");
			Node tagDimensiones = nodes.item(0);
			int ancho = Integer.parseInt( ((Element) tagDimensiones).getAttribute("ancho"));
			int alto = Integer.parseInt(((Element) tagDimensiones).getAttribute("alto"));
			Tablero tableroRecuperado = new Tablero(alto,ancho);
			//--------------------------------------
			
			
			
			//cargar obstaculos y sorpresas
			// no encontre un metodo que solo me devuelva un tag, siempre devuelve lista de tags por si se repiten supongo.
			//me refiero al metodo doc.getElementsByTagName que devuelve una lista.
		    nodes = doc.getElementsByTagName("CantidadDeMovimientosMaximo");
			Node cantidadDeMovimientos = nodes.item(0);
			
			int i = Integer.parseInt( ((Element) cantidadDeMovimientos).getAttribute("maximo"));
			tableroRecuperado.setCantMaxMovimientos(i);
			
			nodes = doc.getElementsByTagName("FactorPuntaje");
			Node factorPuntaje = nodes.item(0);
			tableroRecuperado.setFactorPuntos(Integer.parseInt( ((Element) factorPuntaje).getAttribute("factor")));
			
			//
			
			nodes = doc.getElementsByTagName("Partida");
			Node salida = nodes.item(0);
			
			int esquinaInicial=(Integer.parseInt( ((Element) salida).getAttribute("salida")));
			tableroRecuperado.setEsquinaInicial(tableroRecuperado.getEsquinas().get(esquinaInicial));   
			
			nodes = doc.getElementsByTagName("Meta");
			Node llegada = nodes.item(0);
			int esquinaFinal = (Integer.parseInt( ((Element) llegada).getAttribute("llegada")));
			
			tableroRecuperado.setEsquinaFinal(tableroRecuperado.getEsquinas().get(esquinaFinal));
			//
			
			
			
			NodeList tagObstaculos = doc.getElementsByTagName("Obstaculos");// tag "obstaculos"
			NodeList tagSorpresas = doc.getElementsByTagName("Sorpresa");// tag "sorpresas"
				
			try {
				cargarObstaculos(tagObstaculos,tableroRecuperado);
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}//carga los obstaculos al tablero
			cargarSorpresas(tagSorpresas,tableroRecuperado);// idem pero con sorpresas
		    
		
			 
			 return tableroRecuperado;
		
		
		
		
		
	}
	
	private void cargarObstaculos( NodeList obstaculos, Tablero tablero) throws IllegalAccessException{
		
		
		//cargo los hijos del nodo obstaculo (pozo,piquete,control de la gorra)
		
		
	    for (int i= 0 ; i< obstaculos.getLength();i++){
	    	
	    	Node elemento = obstaculos.item(i);
	    	NodeList unObstaculo =  elemento.getChildNodes();
	    	Cuadra cuadra = cuadraConEsquinas(elemento,tablero);// devuelve la cuadra que tenga las esquinas
	    	
				try {
					Class c;
					c = Class.forName(unObstaculo.item(0).getNodeName() );
					Obstaculo obstaculo = (Obstaculo) c.newInstance();
					cuadra.setObstaculo(obstaculo);
				}
			 catch (InstantiationException e) {
						// TODO Auto-generasted catch block
						e.printStackTrace();
				}
				 catch (ClassNotFoundException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
					
				
			
		
	    	
	    	}
	    	
	    	
	    }
		
	private void cargarSorpresas ( NodeList sorpresas, Tablero tablero){
		
		//por cada Tag Sorpresa, agarro su hijo e instancio la clase correspondiente
		
	    for (int i= 0 ; i< sorpresas.getLength();i++){
	    	
	    	
	    	Node elemento = sorpresas.item(i);
	    	NodeList unaSorpresa =  elemento.getChildNodes();
	    	Cuadra cuadra = cuadraConEsquinas(elemento,tablero);
	    	
	    
	    	
	    	try {
				Class c;
				c = Class.forName(unaSorpresa.item(0).getNodeName() );
				Sorpresa sorpresa = (Sorpresa)c.newInstance();
				cuadra.setSorpresa(sorpresa);
			}
		 catch (InstantiationException  e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
			}
			 catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			 catch (IllegalAccessException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
	    	}
	    	
	    	
	    }

	private Cuadra cuadraConEsquinas(Node elemento, Tablero tablero) {
		
		int EsquinaInicial = Integer.parseInt(((Element) elemento).getAttribute("PosicionA"));
		int EsquinaFinal = Integer.parseInt(((Element) elemento).getAttribute("PosicionB"));
		
		List<Cuadra> cuadras = getCuadras(tablero);
		List<Esquina> esquinas = tablero.getEsquinas();
		Iterator<Cuadra> it= cuadras.iterator();
		Cuadra cuadraElegida = null;
		
		
		while( it.hasNext()){
			
			Cuadra unaCuadra= it.next();
			int posicionEsquinaInicial = esquinas.indexOf(unaCuadra.getEsquinaInicial()); 
			int posicionEsquinaFinal = esquinas.indexOf(unaCuadra.esquinaOpuesta(unaCuadra.getEsquinaInicial()));
			
			if((posicionEsquinaInicial==EsquinaInicial) && (posicionEsquinaFinal==EsquinaFinal)){	
				
				 cuadraElegida = unaCuadra;		
			}	
		}
		
		
		return cuadraElegida;
	}
		
	
	
	private List<Cuadra> getCuadras(Tablero tablero){
		//Cargo todas las cuadras--
	
	    List <Cuadra> cuadras = new ArrayList<Cuadra>();
		List<Esquina> Esquinas = tablero.getEsquinas();
		
		
		for (int i = 0; i< Esquinas.size(); i++){
			cuadras.add(Esquinas.get(i).getCuadraDer());
			cuadras.add(Esquinas.get(i).getCuadraAbajo());
		}
		
		
		
		return cuadras;
	}
	
	private void limpiarCuadras(List<Cuadra> cuadras){
		
		/*Recorro la lista de cuadras y elimino aquellos elementos que tengan una redfrencia a null
		 * y las cuadras que no tengan sopresas ni obstaculos.*/
		
			Iterator<Cuadra> it= cuadras.iterator();
			
			while( it.hasNext()){
				
				Cuadra unaCuadra= it.next();
				if(unaCuadra.esquinaOpuesta(unaCuadra.getEsquinaInicial())==null){	
					it.remove();		
				}	
			}
				
			Iterator<Cuadra> it2=cuadras.iterator();
			
			while(it2.hasNext()){
				
				Cuadra unaCuadra= it2.next();
				if ((unaCuadra.getObstaculo()==null) && (unaCuadra.getSorpresa()==null)){		
					it2.remove();
				}
			}
		
	}
		
	}
	
	

